﻿using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FreeRedis;
using GoodsService.Application.Contract.DTO.Input;
using GoodsService.Application.Contract.DTO.Output;
using GoodsService.Application.Contract.IAppService;
using GoodsService.Domain.Aggregate.Goods;
using GoodsService.Domain.Factory;
using GoodsService.Domain.Repository;
using LeoGemini.Service.Infrastructure.Extension;
using LeoGemini.Service.Infrastructure.Handler.Exception;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;

namespace GoodsService.Application.AppService
{
    public class GoodsAppService : ApplicationService, IGoodsAppService
    {
        private readonly IGoodsRepository _goodsRepository;
        private readonly IGoodsFactory _goodsFactory;
        private readonly RedisClient _cache;

        public GoodsAppService(
            IGoodsRepository goodsRepository,
            IGoodsFactory goodsFactory,
            RedisClient cache)
        {
            _goodsRepository = goodsRepository;
            _goodsFactory = goodsFactory;
            _cache = cache;
        }

        /// <summary>
        /// 分页获取商品id
        /// </summary>
        public async Task<PaginationOutputDto<string>> GetGoodsIdsByPaginationAsync(int p, int n)
        {
            // 分页索引不正确
            if (p < 0)
                throw new BadRequestException(ErrorMessage.PAGINATION_PEGE_ERROR);
            
            var goodsIds = new List<string>(
                _cache.ZRange("goodsIds", p * n, p * n + n - 1)
            );
            if (goodsIds.Count == 0)
            {
                // 从数据库获取
                var goodsPart = await _goodsRepository.OrderBy(goods => goods.CreationTime).Select(
                    goods => new {goods.Id, goods.CreationTime}).Skip(p * n).Take(n).ToListAsync();
                // 缓存结果
                foreach (var data in goodsPart)
                {
                    _cache.ZAdd("goodsIds",
                        data.CreationTime.ToUnixTimeTicks(),
                        data.Id);
                    goodsIds.Add(data.Id);
                }
            }

            var totalNum = await _goodsRepository.CountAsync();

            return new PaginationOutputDto<string>
            {
                Num = totalNum,
                Ids = goodsIds
            };
        }

        /// <summary>
        /// 根据id获取商品
        /// </summary>
        public async Task<GetGoodsReturnDto> GetGoodsByIdAsync(string goodsId)
        {
            var goods = await _cache.CacheOrGetAsync($"goods:{goodsId}",
                async () => await _goodsRepository.FindAsync(goodsId));

            if (goods == null)
                throw new NotFoundException(ErrorMessage.GOODS_NOT_FOUND);

            return ObjectMapper.Map<Goods, GetGoodsReturnDto>(goods);
        }

        /// <summary>
        /// 添加商品
        /// </summary>
        public async Task<GetGoodsReturnDto> CreateGoodsAsync(CreateGoodsInputDto dto)
        {
            // 工厂生产商品
            var goods = _goodsFactory.Produce(
                dto.CollectionId, dto.Title, dto.Type,
                dto.Description, dto.Details, dto.PreViews, dto.Shows);
            
            foreach (var attribute in dto.Attributes)
            {
                // 声明商品属性
                goods.DeclareAttribute(attribute.AttrType, attribute.AttrTitle);
            }

            foreach (var skuDto in dto.Skus)
            {
                // 创建sku
                var sku = new GoodsSku(GuidGenerator.Create(),
                    skuDto.Price, skuDto.Stock, skuDto.Unit, skuDto.IsShowStock);
                
                foreach (var specDto in skuDto.Specs)
                {
                    // 标记库存单元属性
                    sku.MarkSpec(specDto.SpecType, specDto.SpecValue);
                }
                
                // 添加库存单元
                goods.AddSku(sku);
            }

            // 持久化商品
            goods = await _goodsRepository.InsertAsync(goods);
            
            // 添加id到分页缓存
            _cache.ZAdd("collectionIds",
                goods.CreationTime.ToUnixTimeTicks(),
                goods.Id);
            
            return ObjectMapper.Map<Goods, GetGoodsReturnDto>(goods);
        }

        /// <summary>
        /// 删除商品
        /// </summary>
        public async Task DeleteGoodsByIdAsync(string goodsId)
        {
            var cacheKey = $"goods:{goodsId}";

            var goods = await _goodsRepository.FindAsync(goodsId);
            // 数据库移除实体
            await _cache.RemoveAndPersistAsync(
                async () =>
                {
                    // 追踪实体
                    var context = await _goodsRepository.GetDbContextAsync();
                    context.Entry(goods).State = EntityState.Deleted;
                    await _goodsRepository.DeleteAsync(goods);
                }, cacheKey);
            
            // 分页缓存移除实体
            _cache.ZRem("goodsIds", goodsId);
            _cache.ZRem($"collections:{goods.CollectionId}:goodsIds", goodsId);
        }

        /// <summary>
        /// 修改商品
        /// </summary>
        public async Task<GetGoodsReturnDto> EditGoodsByIdAsync(string goodsId, EditGoodsInputDto dto)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// 根据系列id分页获取商品id
        /// </summary>
        public async Task<PaginationOutputDto<string>> GetGoodsIdsByCollectionIdAndPaginationAsync(string collectionId, int p, int n)
        {
            // 分页索引不正确
            if (p < 0)
                throw new BadRequestException(ErrorMessage.PAGINATION_PEGE_ERROR);
            
            var goodsIds = new List<string>(
                _cache.ZRange($"collections:{collectionId}:goodsIds", p * n, p * n + n - 1)
            );
            
            if (goodsIds.Count == 0)
            {
                // 从数据库获取
                var goodsPart = await _goodsRepository
                    .OrderBy(goods => goods.CreationTime)
                    .Where(goods => goods.CollectionId == collectionId)
                    .Select(goods => new {goods.Id, goods.CreationTime}).Skip(p * n).Take(n).ToListAsync();
                // 缓存结果
                foreach (var data in goodsPart)
                {
                    _cache.ZAdd($"collections:{collectionId}:goodsIds",
                        data.CreationTime.ToUnixTimeTicks(),
                        data.Id);
                    goodsIds.Add(data.Id);
                }
            }

            var totalNum = await _goodsRepository.CountAsync();
            
            return new PaginationOutputDto<string>
            {
                Num = totalNum,
                Ids = goodsIds
            };
        }
    }
}